/**
 * Copyright &copy; 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package com.thinkgem.jeesite.modules.gen.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.google.common.collect.Maps;
import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.service.ServiceException;
import com.thinkgem.jeesite.common.utils.FileUtils;
import com.thinkgem.jeesite.common.utils.StringUtils;
import com.thinkgem.jeesite.frame.pm.utils.PmParamUtils;
import com.thinkgem.jeesite.kpoint.core.base.KpointBaseEntity;
import com.thinkgem.jeesite.modules.gen.GenConstants;
import com.thinkgem.jeesite.modules.gen.core.GenFileBean;
import com.thinkgem.jeesite.modules.gen.entity.*;
import com.thinkgem.jeesite.modules.gen.parse.IParse;
import com.thinkgem.jeesite.modules.gen.parse.ParseFactory;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import javax.sql.DataSource;
import java.io.*;
import java.util.Map;
import java.util.Properties;

/**
 * 代码生成工具类
 *
 * @author kongxiangsheng
 * @version 2013-11-16
 */
@Slf4j
public class GenUtils {

    /**
     * 初始化列属性字段
     *
     * @param genTable
     */
    public static void initColumnField(GenTable genTable) {
        for (GenTableColumn column : genTable.getColumnList()) {

            // 如果是不是新增列，则跳过。
            if (StringUtils.isNotBlank(column.getId())) {
                continue;
            }

            // 设置字段说明
            if (StringUtils.isBlank(column.getComments())) {
                column.setComments(column.getName());
            }

            // 设置java类型
            if (StringUtils.containsIgnoreCase(column.getDbType(), "BLOB")) {
                column.setJdbcType("BLOB");
                column.setJavaType("String");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "TEXT")) {
                column.setJdbcType("TEXT");
                column.setJavaType("String");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "CHAR")) {
                column.setJdbcType("VARCHAR");
                column.setJavaType("String");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "DATE") || StringUtils.containsIgnoreCase(column.getDbType(), "TIME")) {
                if (column.getDbType().equalsIgnoreCase("DATE")) {
                    column.setJdbcType("DATE");
                } else {
                    column.setJdbcType("TIMESTAMP");
                }
                column.setJavaType("Date");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "BIGINT")) {
                column.setJdbcType("INTEGER");
                column.setJavaType("Long");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "INT")) {
                column.setJdbcType("INTEGER");
                column.setJavaType("Integer");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "DOUBLE")) {
                column.setJdbcType("DOUBLE");
                column.setJavaType("Double");
            } else if (StringUtils.containsIgnoreCase(column.getDbType(), "DECIMAL")) {
                column.setJdbcType("DOUBLE");
                column.setJavaType("BigDecimal");
            }

            // 设置字段长度
            String[] ss = StringUtils.split(StringUtils.substringBetween(column.getDbType(), "(", ")"), ",");
            if (ss != null && ss.length == 1) {
                column.setDbLength(ss[0]);
            } else {
                column.setDbLength("0");
            }

            // 设置java字段名
            column.setJavaName(StringUtils.toCamelCase(column.getName()));

            // 是否是主键
            column.setIsPk(genTable.getPkList().contains(column.getName()) ? "1" : "0");

            // 编辑字段
            if (!StringUtils.equalsIgnoreCase(column.getName(), "create_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "last_update_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "del_flag")) {
                column.setIsEdit("1");
            }

            // 查看字段
            if (!StringUtils.equalsIgnoreCase(column.getName(), "create_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "last_update_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "del_flag")) {
                column.setIsView("1");
            }

            // 列表字段
            if (!StringUtils.equalsIgnoreCase(column.getName(), "create_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "last_update_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "del_flag")) {
                column.setIsList("1");
            }

            // 查询字段
            if (!StringUtils.equalsIgnoreCase(column.getName(), "id")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "create_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_by")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "update_date")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "last_update_time")
                    && !StringUtils.equalsIgnoreCase(column.getName(), "del_flag")) {
                column.setIsQuery("1");
            }

            // 是否排重字段
            if (StringUtils.equalsIgnoreCase(column.getName(), "name")) {
                column.setIsCheckUnique("1");
            }

            // 查询字段类型
            if (StringUtils.equalsIgnoreCase(column.getName(), "name")
                    || StringUtils.equalsIgnoreCase(column.getName(), "title")) {
                column.setQueryType("like");
            }

            // 设置特定类型和字段名
            // 用户
            if (StringUtils.startsWithIgnoreCase(column.getName(), "user_id")) {
                column.setShowType("userselect");
            }
            // 机构
            else if (StringUtils.startsWithIgnoreCase(column.getName(), "office_id")) {
                column.setShowType("officeselect");
            }
            // 区域
            else if (StringUtils.startsWithIgnoreCase(column.getName(), "area_id")) {
                column.setShowType("areaselect");
            }
            // 创建者、更新者
            else if (StringUtils.startsWithIgnoreCase(column.getName(), "create_by")
                    || StringUtils.startsWithIgnoreCase(column.getName(), "update_by")) {
                column.setShowType("userselect");
            }
            // 删除标记
            else if (StringUtils.equalsIgnoreCase(column.getName(), "del_flag")) {
                column.setShowType("radiobox");
            }

            // 日期类型的字段，自动设置展示类型为日期选择
            if (StringUtils.containsIgnoreCase(column.getDbType(), "DATE") || StringUtils.containsIgnoreCase(column.getDbType(), "TIME")) {
                column.setShowType("dateselect");
            }
            // 逻辑删除字段：自动设置数据字典是del_flag
            if (StringUtils.equalsIgnoreCase(column.getName(), "del_flag")) {
                column.setDictType("del_flag");
            }
        }
    }

    public static Map<String, Object> getDataModel(GenScheme genScheme) {
        Map<String, Object> model = Maps.newHashMap();
        model.put(GenConstants.ParseModel.GLOBAL, ModelUtils.getGlobalModel(genScheme));
        model.put(GenConstants.ParseModel.TABLE, genScheme.getGenTable());

        return model;
    }

    /**
     * 转换文件名
     *
     * @param fileName
     * @param genScheme
     * @return
     */
    public static String parseFileName(String fileName, GenScheme genScheme) {
        GenTable genTable = genScheme.getGenTable();
        GenProject genProject = genScheme.getGenProject();
        GenProjectModule genProjectModule = genScheme.getGenProjectModule();

        // 替换模块名
        fileName = fileName.replaceAll("@module@", null == genProjectModule ? "" : genProjectModule.getModuleCode());

        // 替换表明
        fileName = fileName.replaceAll("@entity@", StringUtils.capitalize(genTable.getClassNameCapital()));

        // 替换项目简称
        String projectShortCodeCapital = StringUtils.toCapitalizeCamelCase(genProject.getShortCode());
        fileName = fileName.replaceAll("@projectShortCode@", StringUtils.uncapitalize(projectShortCodeCapital));
        fileName = fileName.replaceAll("@ProjectShortCode@", projectShortCodeCapital);
        return fileName;
    }

    /**
     * 转换文件路径
     *
     * @param fileLocation
     * @param genScheme
     * @return
     */
    public static String parseFileLocation(String fileLocation, GenScheme genScheme) {
        GenProject genProject = genScheme.getGenProject();
        GenTable genTable = genScheme.getGenTable();
        GenProjectModule genProjectModule = genScheme.getGenProjectModule();

        // 替换模块名和表名
        fileLocation = fileLocation.replaceAll("@finalBasePackage@", genScheme.getBasePackage().toLowerCase());
        fileLocation = fileLocation.replaceAll("@basePackage@", genProject.getBasePackage().toLowerCase());
        fileLocation = fileLocation.replaceAll("@project@", genProject.getCode().toLowerCase());
        fileLocation = fileLocation.replaceAll("@module@", null == genProjectModule ? "" : genProjectModule.getModuleCode());
        fileLocation = fileLocation.replaceAll("@Entity@", StringUtils.capitalize(genTable.getClassNameCapital()));
        fileLocation = fileLocation.replaceAll("@entity@", StringUtils.uncapitalize(genTable.getClassNameCapital()));

        // 将包路径修改成文件路径
        if (StringUtils.isNotEmpty(fileLocation)) {
            fileLocation = fileLocation.replaceAll("\\.", "\\/");
        }
        return fileLocation;
    }

    /**
     * 生成
     *
     * @param genTemplateItem
     * @param model
     * @return
     */
    public static GenFileBean generate(GenScheme genScheme, GenTemplateItem genTemplateItem, Map<String, Object> model) {
        GenProject genProject = genScheme.getGenProject();
        IParse parser = ParseFactory.getParse(genProject.getParseType());

        String fileLocation = parseFileLocation(genTemplateItem.getFileLocation(), genScheme);
        log.debug(" fileLocation === " + fileLocation);

        String fileName = parseFileName(genTemplateItem.getFileName(), genScheme);
        log.debug(" fileName === " + fileName);

        // 获取生成文件内容
        String templateContent = getFileContent(genTemplateItem.getContentPath());
        String fileContent = parser.parse(StringUtils.trimToEmpty(templateContent), model);

        GenFileBean genFileBean = new GenFileBean();
        genFileBean.setFileLocation(fileLocation);
        genFileBean.setFileName(fileName);
        genFileBean.setFileContent(fileContent);
        return genFileBean;
    }

    /**
     * 获取代码模板内容
     *
     * @return
     */
    public static String getFileContent(String path) {
        if (StringUtils.isBlank(path)) {
            return "";
        }

        String tmpFilePath = Global.getUserfilesBaseDir() + path;

        String filePath = FileUtils.path(tmpFilePath);

        try {
            @Cleanup
            InputStream is = new FileInputStream(filePath);
            @Cleanup
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            StringBuilder sb = new StringBuilder();
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    break;
                }
                sb.append(line).append("\r\n");
            }
            return sb.toString();
        } catch (IOException e) {
            throw new ServiceException("读取模板文件[" + filePath + "]内容失败。", e);
        }
    }

    /**
     * 获取SqlSession
     *
     * @return
     */
    public static SqlSessionFactory getSqlSessionFactory() {
        SqlSessionFactory sqlSessionFactory;

        String genDbDriver = PmParamUtils.getParamValue(GenConstants.Param.DB_DRIVER);
        String genDbUrl = PmParamUtils.getParamValue(GenConstants.Param.DB_URL);
        String genDbUsername = PmParamUtils.getParamValue(GenConstants.Param.DB_USERNAME);
        String genDbPassword = PmParamUtils.getParamValue(GenConstants.Param.DB_PASSWORD);
        try {
            //初始化DataSource
            Properties properties = new Properties();
            properties.setProperty("driverClassName", genDbDriver);
            properties.setProperty("url", genDbUrl);
            properties.setProperty("username", genDbUsername);
            properties.setProperty("password", genDbPassword);
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

            // 加载mapper文件
            ClassPathResource resource = new ClassPathResource("/mappings/modules/gen/GenTableMapper.xml");
            Resource[] resources = {resource};

            // 初始化MybatisSqlSessionFactoryBean
            MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
            mybatisSqlSessionFactoryBean.setDataSource(dataSource);
            mybatisSqlSessionFactoryBean.setMapperLocations(resources);
            mybatisSqlSessionFactoryBean.setTypeAliasesPackage("com.thinkgem.jeesite");
            mybatisSqlSessionFactoryBean.setTypeAliasesSuperType(KpointBaseEntity.class);
            mybatisSqlSessionFactoryBean.afterPropertiesSet();

            // 获取sqlSession
            sqlSessionFactory = mybatisSqlSessionFactoryBean.getObject();

        } catch (Exception e) {
            throw new ServiceException("打开代码生成数据库连接失败！", e);
        }

        return sqlSessionFactory;
    }

    /**
     * 获取表的简写名称（即去掉前缀之后的名称）
     *
     * @param tableName
     * @param tablePrefix
     * @return
     */
    public static String genTableShortName(String tableName, String tablePrefix) {
        String name = tableName;
        if (tableName.startsWith(tablePrefix)) {
            name = tableName.substring(tablePrefix.length());
        }
        return name;
    }

    /**
     * 根据表前缀及表明生成表的类名
     *
     * @param tableName
     * @param tablePrefix
     * @return
     */
    public static String genClassNameCapital(String tableName, String tablePrefix) {
        String name = tableName;
        if (tableName.startsWith(tablePrefix)) {
            name = tableName.substring(tablePrefix.length());
        }
        return StringUtils.toCapitalizeCamelCase(name);
    }
}
